package com.snts.synchronization.server.web;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.PropertyConfigurator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.snts.synchronization.SyncService;
import com.snts.synchronization.core.AuthRequest;
import com.snts.synchronization.core.ErrorObject;
import com.snts.synchronization.core.ResponseStatus;
import com.snts.synchronization.core.SyncRequest;
import com.snts.synchronization.core.SyncResponse;
import com.snts.synchronization.protocol.ProtocolException;
import com.snts.synchronization.protocol.adapter.AuthRequestPA;
import com.snts.synchronization.protocol.adapter.AuthResponsePA;
import com.snts.synchronization.protocol.adapter.SyncRequestPA;
import com.snts.synchronization.protocol.adapter.SyncResponsePA;
import com.snts.synchronization.server.SyncServer;

public class SyncServlet extends HttpServlet {

	/**
	 * 
	 */
	
	public static final String SPRING_APPLICATION_CONTEXT_ATTR = "com.snts.synchonization.server.web.APPLICATION_CONTEXT";

	private static final long serialVersionUID = -8416470055962416370L;
	private static final Log log = LogFactory.getLog(SyncServlet.class);

	private ApplicationContext getApplicationContext(HttpServletRequest httpServletRequest){
		return (AbstractApplicationContext) getServletContext().getAttribute(SPRING_APPLICATION_CONTEXT_ATTR);
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		log.debug("SyncServlet.doPost");
		try {
			String url = req.getRequestURL().toString();
			String handlerName = url.substring(url.lastIndexOf('/') + 1);
			if("auth.snc".equals(handlerName)){
				doAuth(req, resp);
			} else if("sync.snc".equals(handlerName)){
				doSync(req, resp);
			} else if("ping.snc".equals(handlerName)){
				doPing(req, resp);
			}
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}

	private void doAuth(HttpServletRequest req, HttpServletResponse resp) throws Exception  {
		SyncServer syncServer = getSyncServer(req);
		AuthRequestPA authRequestPA = getAuthRequestPA(req);
		AuthResponsePA authResponsePA = getAuthResponsePA(req);
		AuthRequest authRequest = authRequestPA.parseMessage(req.getInputStream());
		authResponsePA.serializeMessage(syncServer.auth(authRequest), resp.getOutputStream());
		resp.flushBuffer();
	}

	public AuthResponsePA getAuthResponsePA(HttpServletRequest httpServletRequest) {
		return (AuthResponsePA) getApplicationContext(httpServletRequest).getBean("authResponsePA");
	}

	public AuthRequestPA getAuthRequestPA(HttpServletRequest httpServletRequest) {
		return (AuthRequestPA) getApplicationContext(httpServletRequest).getBean("authRequestPA");
	}
	
	public SyncRequestPA getSyncRequestPA(HttpServletRequest httpServletRequest){
		return (SyncRequestPA) getApplicationContext(httpServletRequest).getBean("syncRequestPA");
	}
	
	public SyncResponsePA getSyncResponsePA(HttpServletRequest req){
		return (SyncResponsePA) getApplicationContext(req).getBean("syncResponsePA");
	}
	
	private SyncServer getSyncServer(HttpServletRequest req){
		return (SyncServer) getApplicationContext(req).getBean("syncServer");
	}

	private void doSync(HttpServletRequest req, HttpServletResponse resp) throws UnsupportedOperationException, Exception {
		SyncService syncServer = getSyncServer(req);
		SyncRequest syncRequest = getSyncRequestPA(req).parseMessage(req.getInputStream());
		log.debug("Got sync request");
		log.debug(syncRequest.toString());
		try {
			SyncResponse syncResponse = syncServer.sync(syncRequest);
			getSyncResponsePA(req).serializeMessage(syncResponse, resp.getOutputStream());
			resp.flushBuffer();  // Client have received update package
			syncServer.confirm(syncResponse);
		} catch(Exception e){
			ErrorObject eo = new ErrorObject();
			eo.setErrorCode(ErrorObject.INTERNAL_ERROR);
			eo.setErrorMessage(e.getMessage());
			eo.setThrowable(e);
			SyncResponse sr = new SyncResponse();
			sr.setStatus(ResponseStatus.FAIL);
			sr.setErrorObject(eo);
			getSyncResponsePA(req).serializeMessage(sr, resp.getOutputStream());
		}
	}

	private void doPing(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		IOUtils.copy(req.getInputStream(), baos);
		String ping = new String(baos.toByteArray());
		ping = ping.substring(ping.indexOf("<png>"), ping.indexOf("</png>"));
		SyncServer syncServer = (SyncServer) getSyncServer(req);
		if(syncServer.ping(ping)){
			resp.getWriter().println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<png>VALID</png>");
		} else {
			resp.getWriter().println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<png>INVALID</png>");
		}
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		resp.getWriter().println("GET is not supported!");
	}

}
